package org.NooLab.glue.net.http.rest.resources;

import java.lang.reflect.Method;
import java.util.Observable;
import java.util.Observer;

import org.NooLab.glue.net.http.rest.RestletMessageSinkIntf;
import org.NooLab.utilities.logging.PrintLog;
import org.restlet.Application;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.resource.ServerResource;

import org.NooLab.utilities.timing.*;

	/** 
	 * Base resource class that supports common behaviours or attributes shared by 
	 * all resources. 
	 *  
	 */  
public abstract class BaseResource extends ServerResource{ 
		
	
	String regAs="";
	String regResult="" ;
	
	Object clientid;
	RestletMessageSinkIntf msgSink;
	
	MessageSupply msgSupply; // implements Observer for prepared registrations, must be served from ...
	
	Application app ;
	ResponseSlot responseSlot;
	// no problem with this: the whole class is running in a thread, which dedicated to single connection

	int timeoutValueByServer = 40000 ;
	
	PrintLog out = new PrintLog(2,true) ;
	
	
	public BaseResource(){
		
	}
	
	
	protected String getUri( int part){
		String str="";
		/*
		 * 	Resource URI  : http://localhost:7070/vp7100/register
			Root URI      : http://localhost:7070/vp7100
			Routed part   : http://localhost:7070/vp7100/register
		 */
		return str;
	}
	protected String getUri(){
		String str;
		
		// 
		str =   "Resource URI  : " + getReference() +"\n" + 
				"Root URI      : " + getRootRef() + "\n" + 
				"Routed part   : " + getReference().getBaseRef() + "\n" + 
				"Remaining part: " + getReference().getRemainingPart();  
		
		return str;
	}
	public void  serveAndGetReturn( MessageSupply msgSupply, String msgText) {
		int nowait=0;
		if (msgText.contains("waitforresponse")){
			nowait=1; // should interpret for attribute value="0"
		}
		serveAndGetReturn(msgSupply, msgText,nowait);
	}
	
	public void  serveAndGetReturn( MessageSupply msgSupply, String msgText, int noWait) {
		
		// System.out.println("Restlet-Server (3), DataHandlerRegPut() ...");
		// now we have to route the request into the standard handlers
		
		msgSupply.serveToMessageBoard(msgText); // :)) this works !!!
		// System.out.println("Restlet-Server (4), DataHandlerRegPut() ...");
		// now we have to wait for the result of the registration
		
		responseSlot = new ResponseSlot();

												// System.out.println("Restlet-Server (5), DataHandlerRegPut() ...");
		if (noWait==0){
			responseSlot.waiting();
		}
		
		
		// String regResult = responseSlot.msg;
		
		msgSink.removeResultsObserver( (Observer) msgSupply);
		 
	}
	
	
	 
	protected MessageSupply prepareMessageRouting(){
		

		Class<?>[] parameterTypes = null; // new Class[]{}; 
		Object[] arguments ;
		arguments = new Object[]{}; 
		Method cbMethod ;
		Observer observingMsgBoard ;
		MessageSupply msgSupply = null;
		
		try{
			app = initObjLinkage();
			
			cbMethod = app.getClass().getMethod("getMessageSink", parameterTypes) ;
			msgSink = ( RestletMessageSinkIntf )cbMethod.invoke( app, arguments ); 
			
			// the implementation is in HttpRestletServer
			observingMsgBoard = msgSink.getObservingMsgBoard(); 
			msgSupply = new MessageSupply( observingMsgBoard );
			
			// over there, we have to keep a map that is linking any callback to this object of msgSupply
			// this msgSupply is unique, since the whole request handler is created per request
			msgSink.importResultsObserver( (Observer)msgSupply ) ;
			// msgSink is in the HttpRestletServer-class
			
			timeoutValueByServer = msgSink.getTimeoutValue();
			
		}catch(Exception e){
			e.printStackTrace();
		}
		return msgSupply;
	}

	protected Application initObjLinkage(){
		Application app;
		
		Context cx = this.getContext();
		Client cli = cx.getClientDispatcher();
		app = cli.getApplication() ;
		return app;
	}
	
	protected String getNameOfParentClass(){
		String nameStr="", str, parentclass;
		Application app;
		
		app = initObjLinkage();
		
		str = app.getName() ;
		// e.g. demo.ResourceBindingForReceptors@182bcde
		// this class hosts the overridden method "createInboundRoot()", hence, there the router has been defined
		parentclass = extractNameofParentClass(str);
		nameStr = parentclass.toLowerCase();
		
		return nameStr;
	}
	
	protected String extractNameofParentClass( String inStr){
		// e.g. demo.ResourceBindingForReceptors@182bcde
		
		String result="" ;
		String str;
		
		int p;
		
		p = inStr.indexOf("@") ;
		
		if (p>0){
			str = inStr.substring(0,p);
			
			p = str.lastIndexOf(".");
			
			if (p>0){
				str = str.substring(p+1,str.length());
			}
			result = str;
		}
		
		return result;
	}
	
	 
	protected String getRegistrationTarget(){
		String parentName ;
		
		// we have to know the the real parent of this class 
		// in order to correctly route the request, either to register it as a SOURCE or as a RECEPTOR
		// it will be described in the XML as well, nevertheless a nice exercise...
		
		parentName = getNameOfParentClass();
		
		if ( parentName.contains("forreceptor")){
			regAs = "RECEPTOR" ;
		}
		if (parentName.contains("forsource")){
			regAs = "SOURCE" ;
		}
		
		return regAs;
	}
	

	class MessageSupply extends Observable implements Observer{
		 
		Observer msObserver ;
		
		public MessageSupply( Observer observer){
			
			msObserver = observer;
			addObserver(observer) ;
			
		}
		
		public void serveToMessageBoard( String strObj){
			
			String str;
			
			str = msObserver.toString() ;
			
			str = this.toString();
			setChanged() ;
			str = "<origin="+str+">::"; 
			                   					// outx out.print(4, "class MessageSupply, before updating the observer, outgoing origin: "+str);
			notifyObservers( str+strObj );
			// will pass through to:  org.NooLab.glue.MessageBoard@2b2af3
			// 
		}

		 
		
		public void update( Observable restletServerObj, Object responseStrObj) {
			 
			String str;
			
			// System.out.println( "Registration Message for Direct Response ... \n");// + (String)responseStrObj) ;
			
			// the problem is, that we could have many observers, thus the
			// messages contains a secret : the object id of the issuing Observer!
			str = (String)responseStrObj ;
			
if (str.contains("<register>")){
	out.print(3, "observed by MessageSupply, preparing response string ...\n\r-"+str) ;
}

			// we route this message ONLY, if the object id matches to the originator secret in the message ! (see just method above!) 
			if (checkMessageSignature( this, str)==false){
				
				if ( str.indexOf( "originator")<0) {
					out.printErr( 3,"Critical failure: originator-id is not indicated in the response string!");
					   out.print( 4,"complete response str is : \n"+str) ;
				}else{
						
					out.printErr( 5,"MessageSupply: message discarded, originator is different from us.");
					out.printErr( 5, "    offered sign in xml.: "+str+"\n\r"+
							     	 "    expected signature:   "+this.toString() );
				}
				return;
			}
			// outx out.print( 4,"MessageSupply: message accepted, originator identified...");
			
			responseSlot.msg = str;
			 
			regResult = responseSlot.msg;
			
			// System.out.println( "Stopping the waiting thread upon the results... \n");
			responseSlot.process_Completed = true;
			responseSlot.enforcedStop = true;
			
			while ( responseSlot.isrunning ){ 
				responseSlot.delay(1) ;
			}
			 
			// this update thread stops here, the data as contained in regResult is handled in
			// the method "DataHandlerPost()"
		}
		
	}
	
	
	protected boolean checkMessageSignature( MessageSupply msClass, String rawXml){
		String observerHomeObj;
		int p1,p2;
		boolean rB = false;

		observerHomeObj = msClass.toString();
		
		p1 = observerHomeObj.lastIndexOf(".");
		
		if (p1 > 0) {
			observerHomeObj = observerHomeObj.substring(p1 + 1,observerHomeObj.length());
		}
		// xml contains sth. like: <originator id="BaseResource$MessageSupply@1286b10" />
		// observerHomeObj is approx. MessageBoard@1f7abae
		
		p1 = rawXml.indexOf(observerHomeObj);
		p2 = rawXml.indexOf("<originator id=") ;
		
		rB = (p1>0) && (p2>0) && (p1>p2) ;

		return rB;
	}
	
	// it will initiate the waiting thread directly upon construction
	class ResponseSlot implements Runnable{
		
		Thread waitthrd;
		int timeOutValue = timeoutValueByServer; // 20000, 7000 is too short, even for a VM !
		
		boolean isrunning=false, process_Completed=false, enforcedStop=false;
		String msg="" ;
		
		public ResponseSlot(){
			
			waitthrd = new Thread(this,"ResponseSlot") ;
			waitthrd.start() ;
		}
		
		public void run() {
			boolean isrunning=true;
			// a threaded loop which can be timed out ...
			int z=0;
			
			try{
				
				while ((isrunning) && (process_Completed==false) && (enforcedStop==false)){
					delay(5) ;
					if (z>timeOutValue/5){
						isrunning=false;
					}
					z++;
				}
				
			}catch(Exception e){
				
			}
			z++;
			// System.out.println( "Waiting thread has been stopped ...");
			process_Completed=true;
		}

		public String getObservation(){
			return msg;
		}

		public void waiting(){
			process_Completed = false;
			while (process_Completed==false){
				delay(2);
			}
		}
		
		@SuppressWarnings("static-access")
		private void delay(int millis){
			
			try {
				Thread.currentThread().sleep(millis);
			} catch (Exception e) {}	
		}
		
	}// ResponseSlot
}
